module.exports = Delegator;

function Delegator(proto, target) {
  //外部如果没有对Delegator进行构造调用的话 一律咬住 转为构造调用
  if (!(this instanceof Delegator)) return new Delegator(proto, target);

  //this必定指向Delegator的实例对象
  //proto 是context往外暴露的对象
  //target 是一个字符串 可能是'response' 也可能是'request'
  this.proto = proto;
  this.target = target;

  //记录代理了多少个方法的执行权
  this.methods = [];
  this.getters = [];
  this.setters = [];
  this.fluents = [];
}

Delegator.prototype.method = function(name){
  //name : attachment
  //proto 是context往外暴露的对象
  //target 是一个字符串 可能是'response'
  var proto = this.proto;
  var target = this.target;
  //this : Delegator的实例对象
  this.methods.push(name);

  //proto["attachment"] = function(){}
  proto[name] = function(){
    //this 是谁 得到当前函数调用位置的调用形式
    //在中间件函数内部 我们可以使用ctx.attachmnet来调用到当前函数 这个this指向ctx!!!!
    //this["response"]["attachment"].apply(this["response"],实参列表)
    //ctx["response"]["attachment"].apply(this["response"],实参列表)
    return this[target][name].apply(this[target], arguments);
};

  return this;
};

Delegator.prototype.access = function(name){
  return this.getter(name).setter(name);
};

Delegator.prototype.getter = function(name){
  //name : writable
  //proto 是context往外暴露的对象
  //target 是一个字符串 可能是'response'
  var proto = this.proto;
  var target = this.target;
  this.getters.push(name);

  //proto.writable
  proto.__defineGetter__(name, function(){
    //this:ctx
    //ctx["response"]["writable"]
    return this[target][name];
  });

  return this;
};

Delegator.prototype.setter = function(name){
  var proto = this.proto;
  var target = this.target;
  this.setters.push(name);

  proto.__defineSetter__(name, function(val){
    return this[target][name] = val;
  });

  return this;
};

Delegator.prototype.fluent = function (name) {
  var proto = this.proto;
  var target = this.target;
  this.fluents.push(name);

  proto[name] = function(val){
    if ('undefined' != typeof val) {
      this[target][name] = val;
      return this;
    } else {
      return this[target][name];
    }
  };

  return this;
};
